﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using FakeItEasy;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sustainalytics.Accounts.BusinessLogic;
using Sustainalytics.Accounts.Service.Controllers;
using Sustainalytics.Accounts.Service.Models;
using Sustainalytics.AuthOperations;
using Sustainalytics.Entities;
using Sustainalytics.Utils;

namespace Sustainalytics.Accounts.Service.Tests
{
    [TestClass]
    public class AccountsControllerTests
    {
        private static readonly Guid AccountId = Guid.NewGuid();
        private static readonly Guid AccountId2 = Guid.NewGuid();
        private static readonly Guid CurrentEsgProfileId1 = Guid.NewGuid();
        private static readonly Guid UpcomingEsgProfileId1 = Guid.NewGuid();

        private static readonly Guid CurrentEsgProfileId2 = Guid.NewGuid();
        private static readonly Guid UpcomingEsgProfileId2 = Guid.NewGuid();

        private readonly List<Account> _testedAccounts = new List<Account>
         {
            new Account
            {
                Id = AccountId,
                CurrentEsgProfile = CurrentEsgProfileId1,
                UpcomingEsgProfile = UpcomingEsgProfileId1,
                Status = Status.Active,
                RelationshipType = RelationshipType.Client,
                   Name = "Sustainalytics",
                    LastCrmSync = DateTime.Now.AddDays(-10),
                     Office = "Timisoara",
                ModifiedOn = DateTime.Now
            },
            new Account
            {
                Id = AccountId2,
                CurrentEsgProfile = CurrentEsgProfileId2,
                UpcomingEsgProfile = UpcomingEsgProfileId2,
                 Status = Status.Inactive,
                RelationshipType = RelationshipType.Client,
                   Name = "OCE",
                    LastCrmSync = DateTime.Now.AddDays(-30),
                     Office = "Timisoara",
                ModifiedOn = DateTime.Now
            },
            new Account
            {
                Id = Guid.NewGuid(),
                CurrentEsgProfile = Guid.NewGuid(),
                UpcomingEsgProfile = Guid.NewGuid(),
                Status = Status.Inactive,
                RelationshipType = RelationshipType.Client,
                   Name = "ACE",
                    LastCrmSync = DateTime.Now.AddDays(-30),
                     Office = "Bucuresti",
                ModifiedOn = DateTime.Now
            },
            new Account
            {
                Id = Guid.NewGuid(),
                CurrentEsgProfile = Guid.NewGuid(),
                UpcomingEsgProfile = Guid.NewGuid(),
                Status = Status.Active,
                RelationshipType = RelationshipType.Client,
                   Name = "WEST",
                    LastCrmSync = DateTime.Now.AddDays(-30),
                     Office = "Timisoara",
                ModifiedOn = DateTime.Now
            }
         };

        [TestMethod]
        public void Get()
        {
            var filter = new ListOptions();
            var accountsManagement = A.Fake<IAccountsManagement>();
            A.CallTo(() => accountsManagement.GetAccounts(
                A<Status?>.Ignored, A<string>.Ignored, A<ListOptions>.Ignored, A<bool>.Ignored))
                .ReturnsLazily((Status? status, string searchText, ListOptions listOptions, bool tier1OrTier2) =>

            {
                    var returned = new List<Account>(_testedAccounts);
                    if (status != null)
                    {
                        returned = returned.Where(c => c.Status == status).ToList();
                    }
                    if (!string.IsNullOrWhiteSpace(searchText))
                    {
                        returned = returned.Where(u => u.Name.ToLowerInvariant().Contains(searchText.ToLowerInvariant())).ToList();
                    }
                    if (listOptions != null)
                    {
                        if (listOptions.OrderBy != null && listOptions.OrderBy.Any())
                        {
                            foreach (var sortOption in listOptions.OrderBy)
                            {
                                var accountsSortBy =
                                    (AccountsSortEnum) Enum.Parse(typeof(AccountsSortEnum), sortOption, true);

                                switch (accountsSortBy)
                                {
                                    case AccountsSortEnum.Name:
                                        returned = listOptions.OrderAscending
                                            ? returned.OrderBy(a => a.Name).ToList()
                                            : returned.OrderByDescending(a => a.Name).ToList();
                                        break;

                                    case AccountsSortEnum.RelationshipType:
                                        returned = listOptions.OrderAscending
                                            ? returned.OrderBy(a => a.RelationshipType).ToList()
                                            : returned.OrderByDescending(a => a.RelationshipType).ToList();
                                        break;

                                    case AccountsSortEnum.AccountManagerName:
                                        returned = listOptions.OrderAscending
                                            ? returned.OrderBy(a => a.AccountManager.FullName).ToList()
                                            : returned.OrderByDescending(a => a.AccountManager.FullName).ToList();
                                        break;

                                    case AccountsSortEnum.Status:
                                        returned = listOptions.OrderAscending
                                            ? returned.OrderBy(a => a.Status).ToList()
                                            : returned.OrderByDescending(a => a.Status).ToList();
                                        break;
                                }
                            }
                        }
                        returned = returned.Skip(listOptions.Skip).Take(listOptions.Take).ToList();
                    }

                    return returned;
                }
                );
            A.CallTo(() => accountsManagement.GetAccountsTotalCount(
                A<Status?>.Ignored, A<string>.Ignored, A<bool>.Ignored))
                .ReturnsLazily(
                    (Status? status, string searchText, bool tier1OrTier2) =>
                {
                        var returned = new List<Account>(_testedAccounts);
                    if (status != null)
                    {
                        returned = returned.Where(c => c.Status == status).ToList();
                    }
                    if (!string.IsNullOrWhiteSpace(searchText))
                    {
                        returned = returned.Where(u => u.Name.ToLowerInvariant().Contains(searchText)).ToList();
                    }

                    return returned.Count;
                    });
            A.CallTo(() => accountsManagement.GetAccountById(
                A<Guid>.Ignored))
                .ReturnsLazily(
                    (Guid userId) => _testedAccounts[0]);
            var authOperations = A.Fake<IAuthOperations>();
            var accountWithIpManager = A.Fake<AccountWithIpManager>();
            using (var accountsController = new AccountsController(accountsManagement, authOperations, accountWithIpManager)
            {
                Request = new HttpRequestMessage(),
                Configuration = new HttpConfiguration()
            })
            {
                List<object> accountsObject;

            //Get active accounts and order them by fullname
                filter.OrderBy = new List<string> {"Name"}.ToArray();
            filter.OrderAscending = false;
                var response = accountsController.Get(filter, false, "Active");
            Assert.IsTrue(response.IsSuccessStatusCode, "The request has failed.");
                Assert.IsTrue(response.TryGetContentValue(out accountsObject),
                    "The response does not contain the list of accounts.");
                Assert.AreEqual(_testedAccounts.Count(u => u.Status.ToString() == "Active"), accountsObject.Count,
                    "The number of active accounts is different than the expected one.");

            //Get all users that contain 'sust' and order them by fullname
                const string filterText = @"Sustainalytics";
            filter = new ListOptions();
            response = accountsController.Get(filter, false, null, filterText);
            Assert.IsTrue(response.IsSuccessStatusCode, "The request has failed.");
                Assert.IsTrue(response.TryGetContentValue(out accountsObject),
                    "The response does not contain the list of users.");
                Assert.AreEqual(_testedAccounts.Count(u => u.Name.Contains(filterText)), accountsObject.Count,
                    "The number of returned accounts are different than the expected one.");

            //Get user by id
                response = accountsController.Get(_testedAccounts[0].Id);
                AccountDto account;
            Assert.IsTrue(response.IsSuccessStatusCode, "The request has failed.");
                Assert.IsTrue(response.TryGetContentValue(out account),
                    "The response does not contain the requested account.");
            Assert.IsNotNull(account, "The account is null.");
                Assert.AreEqual(_testedAccounts.First(a => a.Id == _testedAccounts[0].Id).Id, account.Id,
                    "The id of the retrieved account is different than expected.");

            int accountCount;
                filter = new ListOptions
                {
                    GetCount = true
                };
                response = accountsController.Get(filter);
            Assert.IsTrue(response.IsSuccessStatusCode, "The request has failed.");
                Assert.IsTrue(response.TryGetContentValue(out accountCount),
                    "The response does not contain the number of accounts.");
                Assert.AreEqual(_testedAccounts.Count, accountCount,
                    "The number of returned accounts is different than the expected one.");
            }
        }
    }
}
